package com.asha.vrlib;

import android.content.Context;
import android.graphics.Point;
import android.graphics.SurfaceTexture;
import android.opengl.GLES20;
import android.opengl.GLSurfaceView;
import android.util.Log;

import com.asha.vrlib.common.Fps;
import com.asha.vrlib.common.MDGLHandler;
import com.asha.vrlib.plugins.MDAbsLinePipe;
import com.asha.vrlib.plugins.MDAbsPlugin;
import com.asha.vrlib.plugins.MDBarrelDistortionLinePipe;
import com.asha.vrlib.plugins.MDPluginManager;
import com.asha.vrlib.strategy.display.DisplayModeManager;
import com.asha.vrlib.strategy.projection.ProjectionModeManager;
import com.asha.vrlib.texture.MD360Texture;
import com.codecrecord.AbsRecord;
import com.codecrecord.GrafikaRecord;
import com.codecrecord.TextureMovieEncoder;

import java.nio.ByteBuffer;
import java.util.List;

import javax.microedition.khronos.egl.EGLConfig;
import javax.microedition.khronos.opengles.GL10;

import static com.asha.vrlib.common.GLUtil.glCheck;

/**
 * Created by hzqiujiadi on 16/1/22.
 * hzqiujiadi ashqalcn@gmail.com
 *
 * @see Builder
 * @see #with(Context)
 */
public class MD360Renderer implements GLSurfaceView.Renderer {

	private static final String TAG = "FUYAO_MD360Renderer";
    private static final int FPS_SECONDS = 16 * 1000 * 1000;
	private DisplayModeManager mDisplayModeManager;
	private ProjectionModeManager mProjectionModeManager;
	private MDPluginManager mPluginManager;
	private MDAbsLinePipe mMainLinePipe;
	private MDGLHandler mGLHandler;
	private Fps mFps = new Fps();
	private int mWidth;
	private int mHeight;

	// private MDBarrelDistortionPlugin mBarrelDistortionPlugin;


    private OnCaptureOverListener mOnCaptureOverListener = null;
    private ByteBuffer mCaptureBuffer;
    private boolean isTakePic = false;

//	private static final RecorderHelper mRecorderHelper = RecorderHelper.getInstance();
	private AbsRecord mRecord = new GrafikaRecord();
	// final
	private final Context mContext;

	private MD360Renderer(Builder params){
		mContext = params.context;
		mDisplayModeManager = params.displayModeManager;
		mProjectionModeManager = params.projectionModeManager;
		mPluginManager = params.pluginManager;
		mGLHandler = params.glHandler;

		mMainLinePipe = new MDBarrelDistortionLinePipe(mDisplayModeManager);
	}

    public boolean doCapture(final OnCaptureOverListener callback) {
		mGLHandler.post(new Runnable() {
			@Override
			public void run() {
				if (isTakePic || null == callback) {
					return;
				}
				mOnCaptureOverListener = callback;
				isTakePic = true;
			}
		});
        return true;
    }

	public void destroy() {
		mRecord.release();
	}

	public interface OnCaptureOverListener {
        void onCapture(ByteBuffer buffer, int width, int height);
    }

	@Override
	public void onSurfaceCreated(GL10 glUnused, EGLConfig config){
		// set the background clear color to black.
		GLES20.glClearColor(0.0f, 0.0f, 0.0f, 0.0f);
		
		// use culling to remove back faces.
		GLES20.glEnable(GLES20.GL_CULL_FACE);

		// enable depth testing
		// GLES20.glEnable(GLES20.GL_DEPTH_TEST);
	}

	//thread safe
	public int initRecord(String path, boolean voice){
		if (!mRecord.isRecording()){
			mRecord.setRecordVoice(voice);
			mRecord.setVideoPath(path);
			MD360Texture texture = mProjectionModeManager.getTexture();
			int texId = texture.getCurrentTextureId();
			SurfaceTexture surfaceTexture = texture.getSurfaceTexture();
			if (texId != 0 && surfaceTexture != null){
//				mRecord.initRenderHandler(surfaceTexture);
				mRecord.setTexture(texId, surfaceTexture);
				mRecord.setTextureSize(mWidth, mHeight);
				mRecord.init();
				return 0;
			}
		}
		return -1;
	}

	//thread safe
	public int startRecord(TextureMovieEncoder.OnVideoDone onVideoDone){
		return mRecord.handleStartRecording(onVideoDone);
	}

	//thread safe
	public void stopRecord(){
		mRecord.handleStopRecording();
	}

	private int recordFps = 0;

	private void handleRecordFrame() {
        if (recordFps >= 3){
            recordFps = 0;
        }
		if (recordFps == 0) {
				mRecord.newFrame();
		}
        recordFps++;
	}

	@Override
	public void onSurfaceChanged(GL10 glUnused, int width, int height){
		this.mWidth = width;
		this.mHeight = height;
        mDisplayModeManager.onSurfaceChanged(width, height);
		mRecord.setTextureSize(mWidth, mHeight);
		mGLHandler.dealMessage();
	}

	@Override
	public void onDrawFrame(GL10 glUnused){
        final long nowInNanoSeconds;
        nowInNanoSeconds = System.nanoTime();
		// gl thread
		// 切换策略
		// 热点拾取
		mGLHandler.dealMessage();

		GLES20.glClear(GLES20.GL_COLOR_BUFFER_BIT | GLES20.GL_DEPTH_BUFFER_BIT);
		glCheck("MD360Renderer onDrawFrame 1");

		handleRecordFrame();

		int size = mDisplayModeManager.getVisibleSize();

		List<Point> sizes = mDisplayModeManager.getDisplaySizes();
		List<Point> poss = mDisplayModeManager.getDisplayPoss();

		if (null == sizes || null == poss || size != sizes.size() || size != poss.size()) {
			Log.e(TAG, "onDrawFrame size error0 sizes=" + sizes + " poss=" + poss + " size="+size);
            mDisplayModeManager.updateStrategyViewportInfo();
            sizes = mDisplayModeManager.getDisplaySizes();
            poss = mDisplayModeManager.getDisplayPoss();
			Log.e(TAG, "onDrawFrame size error1 sizes=" + sizes + " poss=" + poss + " size="+size);
        }

		if (size != sizes.size()) {
            size = sizes.size();
			Log.e(TAG, "onDrawFrame size error2 sizes=" + sizes + " poss=" + poss + " size="+size);
        }

		// take over
		mMainLinePipe.setup(mContext);
		mMainLinePipe.takeOver(mWidth,mHeight,size);

		List<MD360Director> directors = mProjectionModeManager.getDirectors();

		// main plugin
		MDAbsPlugin mainPlugin = mProjectionModeManager.getMainPlugin();
		try {
			if (mainPlugin != null){
                mainPlugin.setupInGL(mContext);
                mainPlugin.beforeRenderer(this.mWidth, this.mHeight);
            }

			for (MDAbsPlugin plugin : mPluginManager.getPlugins()) {
                plugin.setupInGL(mContext);
                plugin.beforeRenderer(this.mWidth, this.mHeight);
            }
		} catch (Exception e) {
			Log.e("Log", "beforeRenderer error", e);
		}

		if (isTakePic) {
            isTakePic = false;
			mCaptureBuffer = ByteBuffer.allocate(mWidth * mHeight * 4);
            mCaptureBuffer.rewind();
			GLES20.glPixelStorei(GLES20.GL_PACK_ALIGNMENT, 4);
            GLES20.glReadPixels(0, 0, mWidth, mHeight, GLES20.GL_RGBA, GLES20.GL_UNSIGNED_BYTE,
                    mCaptureBuffer);
			glCheck("MD360Renderer isTakePic");
            mOnCaptureOverListener.onCapture(mCaptureBuffer, mWidth, mHeight);
        }

		int posX, posY, sizeX, sizeY;
		for (int i = 0; i < size; i++){
			if (i >= directors.size()) break;

			posX = poss.get(i).x;
			posY = poss.get(i).y;
			sizeX = sizes.get(i).x;
			sizeY = sizes.get(i).y;
			MD360Director director = directors.get(i);
			//GLES20.glViewport(width * i, 0, width, height);
			GLES20.glViewport(posX, posY, sizeX, sizeY);
			GLES20.glEnable(GLES20.GL_SCISSOR_TEST);
			//GLES20.glScissor(width * i, 0, width, height);
			GLES20.glScissor(posX, posY, sizeX, sizeY);
			try {
				if (mainPlugin != null){
                    //mainPlugin.renderer(i, width, height, director);
                    mainPlugin.renderer(i, sizeX, sizeY, director);
                }

				for (MDAbsPlugin plugin : mPluginManager.getPlugins()) {
                    //plugin.renderer(i, width, height, director);
                    plugin.renderer(i, sizeX, sizeY, director);
                }
			} catch (Exception e) {
				Log.e("Log", "renderer error", e);
			}
			GLES20.glDisable(GLES20.GL_SCISSOR_TEST);
		}

		mMainLinePipe.commit(mWidth,mHeight,size);
        mFps.step();

        final long afterInNanoSeconds = System.nanoTime();
        final long interval = afterInNanoSeconds - nowInNanoSeconds;
//      /*
        // fps controlling
        //Log.e(TAG, "onDrawFrame mark1 interval = " + interval);
        if (interval < FPS_SECONDS) {
            try {
                // because we render it before, so we should sleep twice time interval
//                Log.e(TAG, "onDrawFrame mark1 (FPS_SECONDS - interval) / 100000 = " + ((FPS_SECONDS - interval) / 100000));
                Thread.sleep((FPS_SECONDS - interval) / 1000000);
            } catch (final Exception e) {
            }
        }

    }

	public static Builder with(Context context) {
		Builder builder = new Builder();
		builder.context = context;
		return builder;
	}

	public static class Builder{
		private Context context;
		private DisplayModeManager displayModeManager;
		private ProjectionModeManager projectionModeManager;
		private MDGLHandler glHandler;
		private MDPluginManager pluginManager;

		private Builder() {
		}

		public MD360Renderer build(){
			return new MD360Renderer(this);
		}

		public Builder setGLHandler(MDGLHandler glHandler){
			this.glHandler = glHandler;
			return this;
		}

		public Builder setPluginManager(MDPluginManager pluginManager) {
			this.pluginManager = pluginManager;
			return this;
		}

		public Builder setDisplayModeManager(DisplayModeManager displayModeManager) {
			this.displayModeManager = displayModeManager;
			return this;
		}

		public Builder setProjectionModeManager(ProjectionModeManager projectionModeManager) {
			this.projectionModeManager = projectionModeManager;
			return this;
		}
	}
}
